home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-02
/
asorts.zip
/
ASORTS.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1993-01-04
|
18KB
|
521 lines
unit asorts; {Last modified: 09APR91}
{ General-purpose array manipulation routines }
{ Copyright 1991, J. W. Rider }
{ Notice: This unit makes extensive use of array types that exceed the
maximum "safe" size of 65519 bytes. While the compiler "allows" the
declaration without error, application program should not ordinarily try
to allocate memory to such structures. Segment wraparound problems can
otherwise occur. For instance, most of these routines will not work on an
array that "straddles" a segment boundary. If you notice carefully in
this unit, the large arrays are used only for typecasting purposes, and
no memory is allocated to them. }
interface
{ $define MONITOR} { <--- remove space before "$" to enable
monitoring various sorting routines }
{$ifdef MONITOR}
var monitor : procedure; { for monitoring results of sort }
procedure nullmonitor; { to turn monitoring off }
{$endif}
{ *** Type definitions *** }
{ "comparefunc" -- comparison function argument for "qsort", "bsearch"
"lfind" and "lsearch"
"icomparefunc"-- comparison function argument for "virtual" routines
"swapproc" -- exchange procedure for "virtual" routines
"testfunc" -- test function argument for "scan" }
type comparefunc = function (var a,b):longint;
icomparefunc= function (a,b:longint):longint;
swapproc = procedure(a,b:longint);
testfunc = function (var a):boolean;
{ *** C compatibility routines *** }
{ "qsort", "bsearch", "lfind", "lsearch" and "swab" are analogous to
standard C functions of the same names }
{ quicksort the elements of an array }
procedure qsort(var base; length_base, sizeof_element:word;
f:comparefunc);
{ binary search a sorted array for an element}
function bsearch(var key,base; length_base, sizeof_element:word;
f:comparefunc):word;
{ linear search an array for an element }
function lfind(var key,base; length_base, sizeof_element:word;
f:comparefunc):word;
{ linear search an array for an element; append if not found }
function lsearch(var key,base; length_base, sizeof_element:word;
f:comparefunc):word;
{ move one array of words to another, swapping bytes }
procedure swab(var source, destination; numwords:word);
{ *** "riderized" (i.e, generally nonstandard) routines *** }
{ the remaining routines generally have no standard implementation in other
languages }
{ binary search a sorted array for an element. Return the index of
its location, or the negative of the index where it should be inserted }
function bfind(var key,base; length_base, sizeof_element:word;
f:comparefunc):longint;
{ inserts an element into a sorted array. }
function binsert(var key,base; length_base, sizeof_element:word;
f:comparefunc):word;
{ fibonacci search a sorted array; marginally faster than "bsearch" }
function fibsearch(var key,base; length_base, sizeof_element:word;
f:comparefunc):word;
{ fill an array with an element }
procedure fill(var key,destination; count, sizeof_element:word);
{ order an array by the "heapsort" algorithm }
procedure heapsort(var base; length_base, sizeof_element:word;
f:comparefunc);
{ return the address of variable as a longint value }
function longaddr(var x):longint;
{ a not-so-quick sorting routine, compare with qsort }
procedure naivesort(var base; length_base, sizeof_element:word;
f:comparefunc);
{ scan a subarray for the first element that meets a specific criteria }
function scan(var source; count, sizeof_element:word; f:testfunc):word;
{ order an array by the "selection sort" algorithm }
procedure selsort(var base; length_base, sizeof_element:word;
f:comparefunc);
{ order an array by the "shell sort" algorithm }
procedure shellsort(var base; length_base, sizeof_element:word;
f:comparefunc);
{ randomly permute the elements of an array }
procedure shuffle(var base; length_base, sizeof_element:word);
{ fill a subarray with an element }
procedure subfill(var key,destination;
count, sizeof_key,sizeof_element:word);
{ move subarray to array or array to subarray }
procedure submove(var source,destination;
count, sizeof_source, sizeof_destination:word);
{ swap two elements or variables of the same size }
procedure swap(var var1,var2; sizeof_element:word);
{ sort a "virtual" array by the quicksort algorithm }
procedure vqsort(length_base:longint; f:icomparefunc; s:swapproc);
{ sort a "virtual" array by using a selection sort algorithm }
procedure vselsort(length_base:longint; f:icomparefunc; s:swapproc);
{ randomly permute a "virtual" array }
procedure vshuffle(length_base:longint; s:swapproc);
{ move subarray to subarray }
procedure xsubmove(var source,destination;
count,sizeof_source,sizeof_destination,sizeof_move:word);
implementation
function bfind(var key,base; length_base, sizeof_element:word;
f:comparefunc):longint;
var b:array [0..$fffe] of byte absolute base; l,h,x,c:longint;
begin if length_base>0 then begin
l:=0; h:=pred(length_base);
repeat
x:=(l+h) shr 1; c:=f(key,b[x*sizeof_element]);
if c<0 then h:=pred(x)
else if c>0 then l:=succ(x)
else{if c=0 then}begin bfind:=succ(x); exit; end;
until l>h;
bfind:=-l; end
else bfind:=0; end;
function binsert(var key,base;length_base,sizeof_element:word;
f:comparefunc):word;
var b:array [0..$fffe] of byte absolute base; x:longint;
begin
x:=bfind(key,base,length_base,sizeof_element,f);
if x<=0 then x:=-x else dec(x);
move(b[x*sizeof_element],b[succ(x)*sizeof_element],
(length_base-x)*sizeof_element);
move(key,b[x*sizeof_element],sizeof_element);
binsert:=succ(x); end;
function bsearch(var key,base; length_base, sizeof_element:word;
f:comparefunc):word;
var c:longint;
begin
c:=bfind(key,base,length_base,sizeof_element,f);
if c>0 then bsearch:=c
else bsearch:=0; end;
function fibsearch(var key,base; length_base, sizeof_element:word;
f:comparefunc):word;
var b:array [0..$fffe] of byte absolute base; i,p,q,imax:word; t:longint;
begin
imax:=length_base*sizeof_element;
q:=0; p:=sizeof_element; i:=p+q; { set up for fibonacci sequencing }
while imax>(i+p) do begin q:=p; p:=i; inc(i,q); end;
dec(i,sizeof_element); {zero-base adjustment}
while true do begin
if i<imax then t:=f(key,b[i])
else t:=-1; { simulate "too big" for "out of range" }
if t=0 then begin fibsearch:=succ(i div sizeof_element); exit end
else if t<0 then
if q=0 then begin fibsearch:=0; exit end
else begin dec(i,q); q:=p-q; dec(p,q) end
else { if t>0 then }
if p=sizeof_element then begin fibsearch:=0; exit end
else begin inc(i,q); dec(p,q); dec(q,p) end end end;
procedure fill(var key,destination; count, sizeof_element:word);
var b:array [0..$fffe] of byte absolute destination;
x,moved:word;
begin if count>0 then begin
move(key,destination,sizeof_element);
moved:=1; dec(count); x:=sizeof_element;
while count>moved do begin
move(destination,b[x],x);
dec(count,moved); moved:=moved shl 1; x:=x shl 1; end;
move(destination,b[x],count*sizeof_element); end; end;
procedure heapsort(var base; length_base, sizeof_element:word;
f:comparefunc);
var b: array[0..$fffe] of byte absolute base;
p:pointer; nx:longint; k,kx:word;
procedure aux1(kx:word);
procedure aux2; var jx:word;
begin
while kx<=(nx shr 1) do begin
jx:=kx shl 1;
if (jx<nx) and (f(b[jx],b[jx+sizeof_element])<0) then
inc(jx,sizeof_element);
if f(p^,b[jx])>=0 then exit;
move(b[jx],b[kx],sizeof_element);
{$ifdef MONITOR}
if @monitor<>nil then monitor;
{$endif}
kx:=jx end end;
begin {aux1}
move(b[kx],p^,sizeof_element);
{$ifdef MONITOR}
if @monitor<>nil then monitor;
{$endif}
aux2;
move(p^,b[kx],sizeof_element);
{$ifdef MONITOR}
if @monitor<>nil then monitor;
{$endif}
end;
begin {heapsort}
getmem(p,sizeof_element);
nx:=pred(length_base)*sizeof_element;
for k:=(length_base shr 1) downto 1 do aux1(pred(k)*sizeof_element);
repeat
swap(b[0],b[nx],sizeof_element);
{$ifdef MONITOR}
if @monitor<>nil then begin monitor; monitor; monitor end;
{$endif}
dec(nx,sizeof_element);
aux1(0);
until nx<=0;
freemem(p,sizeof_element) end;
function lfind(var key,base; length_base, sizeof_element:word;
f:comparefunc):word;
var b:array [0..$fffe] of byte absolute base; i,j:word;
begin
j:=0;
for i:=1 to length_base do begin
if f(key,b[j])=0 then begin lfind:=i; exit end;
inc(j,sizeof_element); end;
lfind:=0; end;
function longaddr(var x):longint;
begin longaddr:=(longint(seg(x)) shl 4) + ofs(x); end;
function lsearch(var key,base; length_base, sizeof_element:word;
f:comparefunc):word;
var b:array [0..$fffe] of byte absolute base; i:word;
begin
i:=lfind(key,base,length_base,sizeof_element,f);
if i=0 then begin
move(key,b[length_base*sizeof_element],sizeof_element);
lsearch:=succ(length_base); end
else lsearch:=i; end;
procedure naivesort(var base; length_base, sizeof_element:word;
f:comparefunc);
var b: array[0..$fffe] of byte absolute base;
i,j,l,r:word;
begin
i:=0;
for l:=1 to pred(length_base) do begin
j:=i+sizeof_element;
for r:=succ(l) to length_base do begin
if f(b[i],b[j])>0 then begin
swap(b[i],b[j],sizeof_element);
{$ifdef MONITOR}
if @monitor<>nil then monitor;
{$endif}
end;
inc(j,sizeof_element); end;
inc(i,sizeof_element); end; end;
{$ifdef MONITOR}
{ dummy "monitor" }
procedure nullmonitor; begin pointer((@@monitor)^):=NIL end;
{$endif}
procedure qsort(var base; length_base, sizeof_element:word;
f:comparefunc);
var b: array[0..$fffe] of byte absolute base;
j:longint; x:word; { not preserved during recursion }
procedure sort(l,r: word);
var i:longint;
begin
i:=l*sizeof_element;
while l<r do begin
j:=r*sizeof_element;
x:=((longint(l)+r) SHR 1)*sizeof_element;
while i<j do begin
while f(b[i],b[x])<0 do inc(i,sizeof_element);
while f(b[x],b[j])<0 do dec(j,sizeof_element);
if i<j then begin
swap(b[i],b[j],sizeof_element);
if i=x then x:=j else if j=x then x:=i;
{$ifdef MONITOR}
if @monitor<>nil then monitor;
{$endif}
end;
if i<=j then begin
inc(i,sizeof_element); dec(j,sizeof_element) end; end;
if (l*sizeof_element)<j then sort(l,j div sizeof_element);
l:=i div sizeof_element; end; end;
begin sort(0,pred(length_base)); end; {procedure qsort}
function scan(var source; count, sizeof_element:word; f:testfunc):word;
var b:array[0..$fffe] of byte absolute source;
i,j:word;
begin
j:=0;
for i:=1 to count do begin
if f(b[j]) then begin scan:=i; exit; end;
inc(j,sizeof_element); end;
scan:=0; end;
procedure selsort(var base; length_base, sizeof_element:word;
f:comparefunc);
var b:array[0..$fffe] of byte absolute base;
i,ix,j,jx,k,kx:word;
begin
ix:=0;
for i:=1 to pred(length_base) do begin
kx:=ix; jx:=ix;
for j:=succ(i) to length_base do begin
inc(jx,sizeof_element);
if f(b[jx],b[kx])<0 then kx:=jx end;
if kx<>ix then begin
swap(b[kx],b[ix],sizeof_element);
{$ifdef MONITOR}
if @monitor<>nil then monitor;
{$endif}
end; inc(ix,sizeof_element) end; end;
procedure shellsort(var base; length_base, sizeof_element:word;
f:comparefunc);
var b:array[0..$fffe] of byte absolute base;
p:pointer; h,jx:longint; i,hx,ix:word;
procedure aux; begin
while f(b[jx-hx],p^)>0 do begin
move(b[jx-hx],b[jx],length_base); dec(jx,hx);
{$ifdef MONITOR}
if @monitor<>nil then monitor;
{$endif}
if jx<hx then exit end end;
begin if length_base>0 then begin
getmem(p,length_base);
if p<>nil then begin
h:=1; repeat h:=3*h+1 until h>length_base;
repeat
h:=h div 3; hx:=h*sizeof_element; ix:=hx;
for i:=succ(h) to length_base do begin
move(b[ix],p^,sizeof_element);
{$ifdef MONITOR}
if @monitor<>nil then monitor;
{$endif}
jx:=ix; aux;
if jx<>ix then move(p^,b[jx],sizeof_element);
{$ifdef MONITOR}
if @monitor<>nil then monitor;
{$endif}
inc(ix,sizeof_element) end;
until h=1;
freemem(p,length_base) end end end;
procedure shuffle(var base; length_base, sizeof_element:word);
var b: array[0..$fffe] of byte absolute base;
i,ix,j,jx:word;
begin if length_base>0 then
for i:=pred(length_base) downto 1 do begin
ix:=i*sizeof_element;
j:=random(succ(i));
if i<>j then begin
jx:=j*sizeof_element;
swap(b[ix],b[jx],sizeof_element); end; end; end;
procedure subfill(var key,destination;
count, sizeof_key,sizeof_element:word);
var b:array [0..$fffe] of byte absolute destination; i,j:word;
begin
j:=0;
for i:=1 to count do begin
move(key,b[j],sizeof_key);
inc(j,sizeof_element); end; end;
procedure submove(var source, destination;
count, sizeof_source,sizeof_destination:word);
var sm:word;
begin if sizeof_source=sizeof_destination then
move(source,destination,count*sizeof_source)
else begin
if sizeof_source>sizeof_destination then sm:=sizeof_destination
else sm:=sizeof_source;
xsubmove(source,destination,
count,sizeof_source,sizeof_destination,sm); end; end;
procedure swab(var source, destination; numwords:word);
var a: array [1..$7fff] of word absolute source;
b: array [1..$7fff] of word absolute destination;
i:word;
begin if longaddr(source)>=longaddr(destination) then
for i:=1 to numwords do b[i]:=system.swap(a[i])
else
for i:=numwords downto 1 do b[i]:=system.swap(a[i]) end;
procedure swap(var var1,var2; sizeof_element:word);
type chunk = array [0..$f] of byte;
var a:array [0..$fffe] of byte absolute var1;
b:array [0..$fffe] of byte absolute var2;
ac: array [1..$fff] of chunk absolute var1;
bc: array [1..$fff] of chunk absolute var2;
c:chunk; { swap buffer }
k:byte; x:word;
procedure swapchunk(var e,f:chunk);
begin c:=e; e:=f; f:=c; end;
procedure swapbytes(var e,f; len:byte);
begin move(e,c,len); move(f,e,len); move(c,f,len); end;
begin
for k:=1 to (sizeof_element shr 4) do swapchunk(ac[k],bc[k]);
k:=(sizeof_element and $f);
if k>0 then begin
x:=(sizeof_element and $fff0); swapbytes(a[x],b[x],k); end; end;
procedure vqsort(length_base:longint; f:icomparefunc; s:swapproc);
var j,x:longint; { not preserved during recursion }
procedure sort(l,r:longint);
var i:longint;
begin
i:=l; j:=r;
x:=(i+j) SHR 1;
while i<j do begin
while f(i,x)<0 do inc(i);
while f(x,j)<0 do dec(j);
if i<j then begin
s(i,j);
if i=x then x:=j else if j=x then x:=i; end;
if i<=j then begin inc(i); dec(j) end; end;
if l<j then sort(l,j);
if i<r then sort(i,r); end;
begin sort(1,length_base); end; {procedure vqsort}
procedure vselsort(length_base:longint; f:icomparefunc; s:swapproc);
var i,j,k:longint;
begin for i:=1 to pred(length_base) do begin
k:=i;
for j:=succ(i) to length_base do if f(j,k)<0 then k:=j;
if k<>i then s(k,i) end end;
procedure vshuffle(length_base:longint; s:swapproc);
var i,j:longint;
begin for i:=length_base downto 2 do begin
j:=succ(random(i));
if i<>j then begin s(i,j); end; end; end;
procedure xsubmove(var source,destination;
count,sizeof_source,sizeof_destination,sizeof_move:word);
var a:array [0..$fffe] of byte absolute destination;
b:array [0..$fffe] of byte absolute source;
i,j,k:word; r:boolean;
begin
r:=longaddr(source)>=longaddr(destination);
if r then begin j:=0; k:=0; end
else begin
j:=pred(count)*sizeof_destination; k:=pred(count)*sizeof_source; end;
for i:=1 to count do begin
move(b[k],a[j],sizeof_move);
if r then begin
inc(j,sizeof_destination); inc(k,sizeof_source) end
else begin
dec(j,sizeof_destination); dec(k,sizeof_source) end; end; end;
{$ifdef MONITOR}
begin {initialization}
nullmonitor;
{$endif}
end.